#include <iostream>
#include <sstream>
#include <fstream>
#include <string>
#include <cstring>
#include <cstdio>
#include <ctime>
#include <cstdlib>
#include <queue>

#include "../includes/file_pointer_pack.h"
#include "../includes/register_pack.h"
#include "../block_file.h"
#include "../block.h"
#include "../block_factory.h"


using namespace std;

/* Registro ejemplo */
class KeyVal : public Register{
	private:
		std::string key;
		unsigned int value;
	public:
		KeyVal(const std::string& key,unsigned int value){
			this->key   = key;
			this->value = value;
		}
		KeyVal(const KeyVal& k2){
			this->key   = k2.getKey();
			this->value = k2.getValue();
		}

		void setKey(std::string val){ this->key = val; }
		std::string getKey()const{ return this->key;  }
		unsigned int getValue()const{ return this->value; }

		Register* duplicate()const{
			return (new KeyVal(*this));
		}

		void setFields(const Register& b2){
			const KeyVal& temp = dynamic_cast<const KeyVal&>(b2);
			this->key = temp.getKey();
			this->value = temp.getValue();
		}

		unsigned int getKeySize()const{ return key.length(); }

		unsigned int getSize()const {
			/* value,len cadena,cadena */
			return (sizeof(unsigned int)+sizeof(unsigned int)+key.length());
		};

		bool operator<(const Register& b2)const{
			const KeyVal& temp = dynamic_cast<const KeyVal&>(b2);
			return (this->value<temp.getValue());
		}

		std::ostream& toOstream(std::ostream& out)const{
			out << "\t\t Key   : " << this->key << std::endl;
			out << "\t\t Value : " << this->value << std::endl;
			return out;
		}
};


/* Clase necesaria para poder levantar y bajar a disco */
class KeyValFactory : public RegisterFactory{
	public:
		KeyValFactory(){}
		~KeyValFactory(){}

		/* Dado un array de bytes devuelve un registro KeyVal */
		Register* operator ()(char* data)const{
			char* temp = data;
			
			std::string final;
			unsigned int keySize;			
			memcpy((char*)&keySize,temp,sizeof(unsigned int));		
			temp += sizeof(unsigned int);
			char* keyTemp = new char[keySize+1];
			memset(keyTemp,0,keySize+1);
			memcpy(keyTemp,temp,keySize);
			temp += keySize;
			final = keyTemp;
			delete[] keyTemp;

			unsigned int tempVal;
			memcpy(&tempVal,temp,sizeof(unsigned int));
			return (new KeyVal(final,tempVal));
		}
		
		/* Dado un registro KeyVal y un array de bytes serializa la clase KeyVal en el array de bytes */
		char* operator ()(const Register& reg,char* data)const{
			const KeyVal& kvreg = dynamic_cast<const KeyVal&>(reg);
			char* temp = data;
			unsigned int keySize = kvreg.getKeySize();
			memcpy(temp,(char*)&keySize,sizeof(unsigned int));
			temp+=sizeof(unsigned int);
			memcpy(temp,kvreg.getKey().c_str(),keySize);
			temp+=keySize;

			unsigned int tempValue = kvreg.getValue();
			memcpy(temp,&tempValue,sizeof(unsigned int));
			temp+=sizeof(unsigned int);
						
			return temp;
		}
		KeyValFactory* duplicate()const
		{
			return new KeyValFactory;
		}
};

int main(int argc, char *argv[]){
	KeyValFactory kvf;
	BlockFile bf("./data/bloques.dat",StreamFile::m_read|StreamFile::m_write, kvf,32);
	std::queue<FilePointer*> pointers;


	unsigned int nReg = 10;
	for(unsigned int c=0;c<nReg;++c){
		/* 70 bytes */
		KeyVal kv("3_012345678901234567890123456789",c);
		FilePointer* fp = bf.write(kv);
		fp->toOstream(std::cout);
		pointers.push(fp);
	}
	
	for(unsigned int c=0;c<nReg;++c){
		/* 70 bytes */
		KeyVal kv(" _Corto_ ",c+99);
		pointers.push(bf.write(kv));
	}

	bf.toOstream(std::cout);
	
	while(!pointers.empty()){
		BlockPointer* temp = dynamic_cast<BlockPointer*>(pointers.front());
		pointers.pop();
		bf.free(*temp);
		/*KeyVal* kv = dynamic_cast<KeyVal*>(bf.read(*temp));
		kv->setKey("Ahora Ejemplo es mas bastante mas grande de llo que solia ser antes..");	
		FilePointer* f2 = bf.update(*kv,*temp);		

		delete f2;
		delete kv;*/
		delete temp; 
	}

	//bf.toOstream(std::cout);

	/*for(unsigned int c=0;c<nReg;++c){
		KeyVal kv("Ejemplo",c);
		delete(bf.write(kv));
	}*/

	return 0;
}
